#include "widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    setWindowTitle("机器人通用上位机 V" + QString(software_version));
    init_data();
    init_window();
    init_connect();
    init_layout();
    timer->start(timer_frequence);  //0.2s更新一次
//    this->resize(1920, 1080);
//    this->setFixedSize(1920, 1080);
    this->showMaximized();
}

Widget::~Widget()
{
    this->slot_disconnect();
    this->thread_01->exit();
}

void Widget::init_data()
{
    // 核心
    this->socket = new QTcpSocket(this);
    this->protocol_value = new protocol();
    this->timer = new QTimer(this);

    // 主界面
    edit = new QPlainTextEdit(this);

    tab_page01 = new QWidget(this);
    tab_page02 = new QWidget(this);
    tab_page03 = new QWidget(this);
    tab_page04 = new QWidget(this);
    tab_page05 = new QWidget(this);
    tab = new QTabWidget(this);
    tab->addTab(tab_page01, "底盘");
    tab->addTab(tab_page02, "IMU");
    tab->addTab(tab_page03, "电源");
    tab->addTab(tab_page04, "雷达");
    tab->addTab(tab_page05, "升级");

    // 底盘第一列
    ult = new My_group("超声波", this);
    edge = new My_double("防撞条", "前触边", "后触边", this);
    wheel_state = new QGroupBox("轮子状态");
    wheel_state->setAlignment(Qt::AlignCenter);
    line_state = new My_group("前进轮", this);
    line_state->setFlat(true);
    turn_state = new My_group("转向轮", this);
    turn_state->setFlat(true);

    // 底盘第二列
    mc6c = new My_double("遥控器状态", "状态", "模式", this);
    wheel_encoder = new QGroupBox("轮子编码器");
    wheel_encoder->setAlignment(Qt::AlignCenter);
    line_v = new My_group("前进轮线速度(m/s)", this);
    line_v->setFlat(true);
    turn_deg = new My_group("转向轮转向角(度)", this);
    turn_deg->setFlat(true);
    encoder = new My_group("编码器数值", this);
    encoder->setFlat(true);

    // 底盘第三列
    odom = new QGroupBox("里程计");
    odom->setAlignment(Qt::AlignCenter);
    distance = new My_double("运动距离(m)", "x方向", "y方向", this);
    distance->setFlat(true);
    speed = new My_double("速度(m/s)", "x方向", "y方向", this);
    speed->setFlat(true);
    degree = new My_double("角", "角速度", "航向角", this);
    degree->setFlat(true);
    flag = new My_double("标志", "模式", "停车", this);
    flag->setFlat(true);

    odom_clear = new QGroupBox("里程计清零");
    odom_clear->setFlat(true);
    odom_clear->setAlignment(Qt::AlignCenter);
    odom_clear_x = new My_spin("x方向(m)", this);
    odom_clear_y = new My_spin("y方向(m)", this);
    odom_clear_yaw = new My_spin("航向角(度)", this);
    btn_reset_odom = new QPushButton("重置里程计", this);

    // 底盘第四列
    ack_control = new QGroupBox("阿克曼控制");
    ack_control->setAlignment(Qt::AlignCenter);
    ack_mode = new My_Combo("模式", this);
    ack_v = new My_spin("车体速度(m/s)", this);
    ack_w = new My_spin("角速度(rad/s)", this);
    ack_r = new My_spin("旋转半径(m)", this);
    ack_deg = new My_spin("朝向角(度)", this);
    btn_ack_speed = new QPushButton("发速度");
    btn_ack_continue = new QPushButton("连发速度");
    btn_ack_stop = new QPushButton("急停");
    fine_control = new QGroupBox("精细控制");
    fine_control->setAlignment(Qt::AlignCenter);
    fine_speed = new My_spin_group("速度");
    fine_speed->setFlat(true);
    fine_degree = new My_spin_group("转角");
    fine_degree->setFlat(true);
    btn_fine_speed = new QPushButton("发速度", this);
    btn_fine_continue = new QPushButton("连发速度", this);
    btn_fine_stop = new QPushButton("急停", this);

    // 底盘第五列
    motion_mode = new My_combo_send("运动模式", this);
    cmd = new My_combo_send("控制命令", this);
    steer = new My_switch2("舵机", this);
    calibration = new My_spin_btn_group("轮子校准", this);
    btn_emergency_stop = new QPushButton("急停", this);
    btn_emergency_stop->setObjectName("btn_emergency_stop");

    // IMU控件
    imu_degree = new My_triaxial("角度", QStringList{"横滚角", "俯仰角", "航向角"}, this);
    imu_acc = new My_triaxial("加速度", QStringList{"x加速度", "y加速度", "z加速度"}, this);
    imu_gyro = new My_triaxial("角速度", QStringList{"x角速度", "y角速度", "z角速度"}, this);
    imu_magic = new My_triaxial("磁力计", QStringList{"x磁力计", "y磁力计", "z磁力计"}, this);

    // 电池
    battery = new QGroupBox("电池数据");
    charge_flag = new My_label("充电标志", this);
    current = new My_label("电流值", this);
    voltage = new My_label("电压值", this);
    remain_power = new My_label("剩余电量", this);
    sum_power = new My_label("总电量", this);
    percent = new My_label("剩余百分比", this);

    for(auto sw : list_sw_name)
    {
        list_sw.append(new My_switch(sw, this));
    }

    for(int i = 0; i< RELAY_NUM; i++)
    {
        list_relay.append(new My_switch("继电器" + QString::number(i + 1), this));
    }

    lcd_light = new QLCDNumber(this);
    btn_light = new QPushButton("切换状态灯", this);

    btn_set_path = new QPushButton("设置路径", this);
    line_log = new QLineEdit(this);
    radio_log = new QRadioButton("勾选保存日志", this);

    // 雷达控件
    radar_front = new QTableView(this);
    radar_back = new QTableView(this);
    group_radar_front = new QGroupBox("车前雷达", this);
    group_radar_back = new QGroupBox("车后雷达", this);
    table_mode_f = new QStandardItemModel(this);
    table_mode_b = new QStandardItemModel(this);

    // 升级
    update_wig = new My_update(this);
    thread_01 = new QThread(this);
    update_thread = new My_update_thread(); // 要移动到别的线程中的，不能加父指针

    // 右上角
    lcd_group = new QGroupBox("刷新率 单位:Hz");
    lcd_dipan_heart = new My_lcd("底盘心跳", this);
    lcd_ult = new My_lcd("超声波", this);
    lcd_other = new My_lcd("其他", this);
    lcd_encoder = new My_lcd("编码器", this);
    lcd_speed = new My_lcd("里程计", this);
    lcd_dipan_reply = new My_lcd("底盘返回", this);
    lcd_imu = new My_lcd("IMU", this);
    lcd_dipan_status = new My_lcd("开关状态", this);

    lcd_dianyuan_heart = new My_lcd("电源心跳", this);
    lcd_battery = new My_lcd("电池", this);
    lcd_dianyuan_status = new My_lcd("开关状态", this);
    lcd_dianyuan_reply = new My_lcd("电源返回", this);
    lcd_radar = new My_lcd("雷达", this);

    // 底部控件
    lab_ip = new QLabel("IP地址:", this);
    lab_ip->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
    line_ip = new QLineEdit(this);
    lab_port = new QLabel("端口:", this);
    lab_port->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
    line_port = new QLineEdit(this);
    combo_tcpip = new QComboBox(this);
    btn_connect = new QPushButton("连接", this);
    line_send = new QLineEdit(this);
    btn_send = new QPushButton("发送", this);
    btn_clear = new QPushButton("清除", this);
}

void Widget::init_window()
{
    btn_connect->setText("连接");
    btn_connect->setStyleSheet("color:#0000ff");
    set_btn_enable(false);

    line_log->setText(logpath);
    odom_clear_x->setSingleStep(1);
    odom_clear_y->setSingleStep(1);
    odom_clear_yaw->setSingleStep(1);

    ack_v->setSingleStep(0.01);
    ack_w->setSingleStep(0.01);
    ack_deg->setSingleStep(0.01);
    ack_r->setSingleStep(1);

    fine_speed->setSingleStep(0.01);
    fine_degree->setSingleStep(0.01);

    odom_clear_x->setMinimum(-1000);
    odom_clear_y->setMinimum(-1000);
    odom_clear_yaw->setMinimum(-1000);

    ack_v->setMinimum(-1000);
    ack_w->setMinimum(-1000);
    ack_deg->setMinimum(-1000);
    ack_r->setMinimum(-1000);

    fine_speed->setMinimum(-1000);
    fine_degree->setMinimum(-1000);

    combo_tcpip->addItems(strlist_IP);
    slot_comboBox(strlist_IP.at(0));

    ack_mode->addItems(strlist_ack_mode);
    ack_mode->setCurrentIndex(1);

    motion_mode->addItems(strlist_motion_mode);
    cmd->addItems(strlist_cmd);

    table_mode_f->setHorizontalHeaderLabels(strlist_radar);
    table_mode_b->setHorizontalHeaderLabels(strlist_radar);
    radar_front->setModel(table_mode_f);
    radar_back->setModel(table_mode_b);

    calibration->setMinimum(-1000);
    calibration->setSingleStep(0.01);

    status_clear();
}

void Widget::init_connect()
{
    // 发送连接
    connect(btn_connect, SIGNAL(clicked()), this, SLOT(slot_connect()));
    connect(btn_send, SIGNAL(clicked()), this, SLOT(slot_send()));
    connect(btn_clear, SIGNAL(clicked()), edit, SLOT(clear()));

    //网络
    connect(socket, &QTcpSocket::readyRead, this, &Widget::slot_socket_read_data);
    connect(socket, &QTcpSocket::disconnected, this, &Widget::slot_disconnect);

    //事件
    connect(timer, &QTimer::timeout, this, &Widget::slot_timeout);

    //下拉菜单
    connect(combo_tcpip, SIGNAL(activated(QString)), this, SLOT(slot_comboBox(QString)));

    //按键
    connect(btn_reset_odom, &QPushButton::clicked, this, &Widget::slot_pushButton_reset_odom);
    connect(btn_ack_speed, &QPushButton::clicked, this, &Widget::slot_pushButton_send_ackerman);
    connect(btn_ack_stop, &QPushButton::clicked, this, &Widget::slot_pushButton_send_ackerman_stop);
    connect(btn_fine_speed, &QPushButton::clicked, this, &Widget::slot_pushButton_send_fine);
    connect(btn_fine_stop, &QPushButton::clicked, this, &Widget::slot_pushButton_send_fine_stop);
    connect(btn_emergency_stop, &QPushButton::clicked, this, &Widget::slot_pushButton_send_ackerman_stop);
    connect(motion_mode->btn, &QPushButton::clicked, this, &Widget::slot_pushButton_send_mode);
    connect(cmd->btn, &QPushButton::clicked, this, &Widget::slot_pushButton_send_cmd);
    connect(btn_set_path, &QPushButton::clicked, this, &Widget::slot_set_log_path);
    connect(btn_light, &QPushButton::clicked, this, &Widget::slot_set_light);

    int i = 0;
    for(auto it : list_sw)
    {
        connect(it->btn_open,  &QPushButton::clicked, this, [=](){ switch_send(i, 1); });
        connect(it->btn_close, &QPushButton::clicked, this, [=](){ switch_send(i, 0); });
        i++;
    }
    connect(steer->btn_open,  &QPushButton::clicked, this, [=](){ steer_send(1); });
    connect(steer->btn_close, &QPushButton::clicked, this, [=](){ steer_send(0); });

    // 升级
    connect(update_wig->btn_version, &QPushButton::clicked, this, &Widget::slot_version);
    connect(update_wig->btn_update, &QPushButton::clicked, this, &Widget::slot_update);
    connect(update_wig->btn_path, &QPushButton::clicked, this, &Widget::slot_getfile);

    // 多线程
    connect(this, &Widget::signal_go_update, update_thread, &My_update_thread::slot_process);
    connect(update_thread, &My_update_thread::signal_send_date, this, &Widget::slot_send_date);
    connect(update_thread, &My_update_thread::signal_erase_flash, this, &Widget::slot_erase_flash);
    connect(update_thread, &My_update_thread::signal_update_successful, this, &Widget::slot_update_successful);
    update_thread->moveToThread(thread_01);
    thread_01->start();
}

void Widget::init_layout()
{
    // 底盘第一列
    QVBoxLayout* wheel_layout = new QVBoxLayout();
    wheel_layout->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    wheel_layout->setSpacing(SPACE);
    wheel_layout->addWidget(line_state);
    wheel_layout->addWidget(turn_state);
    wheel_state->setLayout(wheel_layout);

    QVBoxLayout* page01_1 = new QVBoxLayout();
    page01_1->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    page01_1->setSpacing(SPACE);
    page01_1->addWidget(ult, 2);
    page01_1->addWidget(edge, 1);
    page01_1->addWidget(wheel_state, 4);
    page01_1->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));

    // 底盘第二列
    QVBoxLayout* encoder_layout = new QVBoxLayout();
    encoder_layout->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    encoder_layout->setSpacing(SPACE);
    encoder_layout->addWidget(line_v);
    encoder_layout->addWidget(turn_deg);
    encoder_layout->addWidget(encoder);
    wheel_encoder->setLayout(encoder_layout);

    QVBoxLayout* page01_2 = new QVBoxLayout();
    page01_2->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    page01_2->setSpacing(SPACE);
    page01_2->addWidget(mc6c, 1);
    page01_2->addWidget(wheel_encoder, 6);

    // 底盘第三列
    QVBoxLayout* odom_clear_layout = new QVBoxLayout();
    odom_clear_layout->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    odom_clear_layout->setSpacing(SPACE);
    odom_clear_layout->addWidget(odom_clear_x);
    odom_clear_layout->addWidget(odom_clear_y);
    odom_clear_layout->addWidget(odom_clear_yaw);
    odom_clear_layout->addWidget(btn_reset_odom);
    odom_clear->setLayout(odom_clear_layout);

    QVBoxLayout* odom_layout = new QVBoxLayout();
    odom_layout->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    odom_layout->setSpacing(SPACE);
    odom_layout->addWidget(distance, 1);
    odom_layout->addWidget(speed, 1);
    odom_layout->addWidget(degree, 1);
    odom_layout->addWidget(flag, 1);
    odom_layout->addWidget(odom_clear, 3);
    odom->setLayout(odom_layout);

    // 底盘第四列
    QHBoxLayout* ack_btn_lay = new QHBoxLayout();
    ack_btn_lay->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    ack_btn_lay->setSpacing(SPACE);
    ack_btn_lay->addWidget(btn_ack_speed);
    ack_btn_lay->addWidget(btn_ack_continue);
    ack_btn_lay->addWidget(btn_ack_stop);

    QVBoxLayout* ack_layout = new QVBoxLayout();
    ack_layout->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    ack_layout->setSpacing(SPACE);
    ack_layout->addWidget(ack_mode);
    ack_layout->addWidget(ack_v);
    ack_layout->addWidget(ack_w);
    ack_layout->addWidget(ack_r);
    ack_layout->addWidget(ack_deg);
    ack_layout->addLayout(ack_btn_lay);
    ack_control->setLayout(ack_layout);

    QHBoxLayout* fine_btn_lay = new QHBoxLayout();
    fine_btn_lay->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    fine_btn_lay->setSpacing(SPACE);
    fine_btn_lay->addWidget(btn_fine_speed);
    fine_btn_lay->addWidget(btn_fine_continue);
    fine_btn_lay->addWidget(btn_fine_stop);

    QVBoxLayout* fine_layout = new QVBoxLayout();
    fine_layout->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    fine_layout->setSpacing(SPACE);
    fine_layout->addWidget(fine_speed);
    fine_layout->addWidget(fine_degree);
    fine_layout->addLayout(fine_btn_lay);
    fine_control->setLayout(fine_layout);

    QVBoxLayout* page01_4 = new QVBoxLayout();
    page01_4->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    page01_4->setSpacing(SPACE);
    page01_4->addWidget(ack_control);
    page01_4->addWidget(fine_control);


    // 底盘第五列
    QVBoxLayout* page01_5 = new QVBoxLayout();
    page01_5->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    page01_5->setSpacing(SPACE);
    page01_5->addWidget(motion_mode, 1);
    page01_5->addWidget(cmd, 1);
    page01_5->addWidget(steer, 1);
    page01_5->addWidget(calibration, 2);
    page01_5->addStretch(4);
//    page01_5->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));
    page01_5->addWidget(btn_emergency_stop, 1);

    // 底盘tab
    QHBoxLayout* page01 = new QHBoxLayout();
    page01->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    page01->setSpacing(SPACE);
    page01->addLayout(page01_1);
    page01->addLayout(page01_2);
    page01->addWidget(odom);
    page01->addLayout(page01_4);
    page01->addLayout(page01_5);
    tab_page01->setLayout(page01);

    // IMU页
    QHBoxLayout* imu_box = new QHBoxLayout();
    imu_box->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    imu_box->setSpacing(SPACE);
    imu_box->addWidget(imu_degree, 1);
    imu_box->addWidget(imu_acc, 1);
    imu_box->addWidget(imu_gyro, 1);
    imu_box->addWidget(imu_magic, 1);
    imu_box->addStretch(2);
    QVBoxLayout* imu_box2 = new QVBoxLayout();
    imu_box2->addLayout(imu_box, 1);
    imu_box2->addStretch(1);
    tab_page02->setLayout(imu_box2);

    // 电源页
    QVBoxLayout* battery_lay = new QVBoxLayout();
    battery_lay->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    battery_lay->setSpacing(SPACE);
    battery_lay->addWidget(charge_flag);
    battery_lay->addWidget(current);
    battery_lay->addWidget(voltage);
    battery_lay->addWidget(remain_power);
    battery_lay->addWidget(percent);
    battery_lay->addWidget(sum_power);
    battery->setLayout(battery_lay);

    QVBoxLayout* sw_lay = new QVBoxLayout();
    sw_lay->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    sw_lay->setSpacing(SPACE);
    for (auto it : list_sw)
    {
        sw_lay->addWidget(it);
    }

    QHBoxLayout* light_lay = new QHBoxLayout();
    light_lay->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    light_lay->setSpacing(SPACE);
    light_lay->addWidget(lcd_light);
    light_lay->addWidget(btn_light);

    QVBoxLayout* light1_lay = new QVBoxLayout();
    light1_lay->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    light1_lay->setSpacing(SPACE);
    for (auto it : list_relay)
    {
        light1_lay->addWidget(it);
    }
    light1_lay->addSpacerItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding));
    light1_lay->addLayout(light_lay);

    QHBoxLayout* bat_lay = new QHBoxLayout();
    bat_lay->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    bat_lay->setSpacing(SPACE);
    bat_lay->addWidget(battery);
    bat_lay->addLayout(sw_lay);
    bat_lay->addLayout(light1_lay);

    QHBoxLayout* log_lay = new QHBoxLayout();
    log_lay->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    log_lay->setSpacing(SPACE);
    log_lay->addWidget(btn_set_path);
    log_lay->addWidget(line_log);
    log_lay->addWidget(radio_log);

    QVBoxLayout* page3_lay = new QVBoxLayout();
    page3_lay->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    page3_lay->setSpacing(SPACE);
    page3_lay->addLayout(bat_lay);
    page3_lay->addLayout(log_lay);

    tab_page03->setLayout(page3_lay);

    // 雷达控件
    QGridLayout* radar_front_lay = new QGridLayout();
    radar_front_lay->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    radar_front_lay->setSpacing(SPACE);
    radar_front_lay->addWidget(radar_front);
    group_radar_front->setAlignment(Qt::AlignCenter);
    group_radar_front->setLayout(radar_front_lay);

    QGridLayout* radar_back_lay = new QGridLayout();
    radar_back_lay->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    radar_back_lay->setSpacing(SPACE);
    radar_back_lay->addWidget(radar_back);
    group_radar_back->setAlignment(Qt::AlignCenter);
    group_radar_back->setLayout(radar_back_lay);

    QHBoxLayout* radar_lay = new QHBoxLayout();
    radar_lay->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    radar_lay->setSpacing(SPACE);
    radar_lay->addWidget(group_radar_front);
    radar_lay->addWidget(group_radar_back);
    tab_page04->setLayout(radar_lay);

    QHBoxLayout* update_lay_h = new QHBoxLayout();
    update_lay_h->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    update_lay_h->setSpacing(SPACE);
    update_lay_h->addStretch(1);
    update_lay_h->addWidget(update_wig, 1);
    update_lay_h->addStretch(1);
    QVBoxLayout* update_lay_v = new QVBoxLayout();
    update_lay_v->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    update_lay_v->setSpacing(SPACE);
    update_lay_v->addStretch(1);
    update_lay_v->addLayout(update_lay_h, 1);
    update_lay_v->addStretch(1);

    tab_page05->setLayout(update_lay_v);

    // 右上角
    QGridLayout* lcd_lay = new QGridLayout();
    lcd_lay->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    lcd_lay->setSpacing(SPACE);
    lcd_lay->addWidget(lcd_dipan_heart, 0, 0);
    lcd_lay->addWidget(lcd_ult, 1, 0);
    lcd_lay->addWidget(lcd_other, 2, 0);
    lcd_lay->addWidget(lcd_encoder, 3, 0);
    lcd_lay->addWidget(lcd_speed, 0, 1);
    lcd_lay->addWidget(lcd_dipan_reply, 1, 1);
    lcd_lay->addWidget(lcd_imu, 2, 1);
    lcd_lay->addWidget(lcd_dipan_status, 3, 1);
    lcd_lay->addWidget(lcd_dianyuan_heart, 0, 2);
    lcd_lay->addWidget(lcd_battery, 1, 2);
    lcd_lay->addWidget(lcd_dianyuan_status, 2, 2);
    lcd_lay->addWidget(lcd_dianyuan_reply, 3, 2);
    lcd_lay->addWidget(lcd_radar, 0, 3);
    lcd_group->setLayout(lcd_lay);

    // 头部
    QHBoxLayout* head = new QHBoxLayout();
    head->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    head->setSpacing(SPACE);
    head->addWidget(edit, 2);
    head->addWidget(lcd_group, 1);

    // 底部
    QGridLayout* bottom = new QGridLayout();
    bottom->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    bottom->setSpacing(SPACE);
    bottom->addWidget(lab_ip, 0, 0, 1, 1);
    bottom->addWidget(line_ip, 0, 1, 1, 9);
    bottom->addWidget(lab_port, 0, 10, 1, 1);
    bottom->addWidget(line_port, 0, 11, 1, 9);
    bottom->addWidget(combo_tcpip, 0, 20, 1, 2);
    bottom->addWidget(btn_connect, 0, 22, 1, 2);

    bottom->addWidget(line_send, 1, 0, 1, 20);
    bottom->addWidget(btn_send, 1, 20, 1, 2);
    bottom->addWidget(btn_clear, 1, 22, 1, 2);

    // 整体布局
    QVBoxLayout* layout = new QVBoxLayout();
    layout->setContentsMargins(LEFT_M, TOP_M, RIGHT_M, BOTTOM_M);
    layout->setSpacing(SPACE);
    layout->addLayout(head, 0);
    layout->addWidget(tab, 1);
    layout->addLayout(bottom, 0);
    this->setLayout(layout);
}

void Widget::write_log(QString str)
{
    qDebug() << str;
}

void Widget::set_btn_enable(bool flag)
{
    btn_reset_odom->setEnabled(flag);
    btn_ack_speed->setEnabled(flag);
    btn_ack_continue->setEnabled(flag);
    btn_ack_stop->setEnabled(flag);
    btn_fine_speed->setEnabled(flag);
    btn_fine_continue->setEnabled(flag);
    btn_fine_stop->setEnabled(flag);
    motion_mode->setEnabled(flag);
    steer->setEnabled(flag);
    cmd->setEnabled(flag);
    btn_emergency_stop->setEnabled(flag);
    btn_send->setEnabled(flag);
    for(auto it : list_sw)
    {
        it->setEnabled(flag);
    }
    for(auto it : list_relay)
    {
        it->setEnabled(flag);
    }
    btn_light->setEnabled(flag);
}

void Widget::status_clear()
{
    // 刷新率
    lcd_dipan_heart->display(0);
    lcd_ult->display(0);
    lcd_other->display(0);
    lcd_encoder->display(0);
    lcd_speed->display(0);
    lcd_dipan_reply->display(0);
    lcd_imu->display(0);
    lcd_dipan_status->display(0);
    lcd_dianyuan_heart->display(0);
    lcd_battery->display(0);
    lcd_dianyuan_reply->display(0);
    lcd_dianyuan_status->display(0);
    lcd_radar->display(0);
    // 超声
    ult->setNum(0, 0, 0, 0);
    // 触边
    edge->setNum(0, 0);
    // 轮子状态 0正常 1失联 2错误
    line_state->setText("失联", "失联", "失联", "失联");
    turn_state->setText("失联", "失联", "失联", "失联");
    // 遥控 param1: 1 在线 0 离线 param2: 0 自动 1 手动 2 停止
    mc6c->setText("离线", "停止");
    // 编码器
    line_v->setNum(0, 0, 0, 0);
    turn_deg->setNum(0, 0, 0, 0);
    encoder->setNum(0, 0, 0, 0);
    // 里程计
    distance->setNum(0, 0);
    speed->setNum(0, 0);
    degree->setNum(0, 0);
    //param1: 1四轮ackman 2两轮ackman 3差速  param2: 0正常 1停车
    flag->setText("四轮ackman", "正常");
    // 舵机
    steer->set_status(-1);
    // IMU
    imu_degree->setNum(0, 0, 0);
    imu_acc->setNum(0, 0, 0);
    imu_gyro->setNum(0, 0, 0);
    imu_magic->setNum(0, 0, 0);
    // 电池
    charge_flag->set_num(0);
    current->set_num(0);
    voltage->set_num(0);
    remain_power->set_num(0);
    sum_power->set_num(0);
    percent->set_num(0);

    for(auto it : list_sw)
    {
        it->set_status(-1);
    }

    for(auto it : list_relay)
    {
        it->set_status(-1);
    }

    // 升级
    this->update_wig->line_path->setText(filepath);

}

void Widget::switch_send(uint8_t type, uint8_t control)
{
    this->write_log(__FUNCTION__);
    QByteArray data = protocol_value->protocol_switch_pack(type, control);
    send(data);
}

void Widget::steer_send(uint8_t control)
{
    this->write_log(__FUNCTION__);
    QByteArray data = protocol_value->protocol_pc_dipan_steer_engine_pack(control);
    send(data);
}

void Widget::note_battery(float voltage, float current, float present, float leave_power)
{
    static int count = 0;
    if(count >= 1000/timer_frequence/log_frequence) {
        count = 0;
        if(radio_log->isChecked() == true)
        {
            QFile file(logpath);
            file.open(QIODevice::WriteOnly | QIODevice::Append);
            QTextStream text_stream(&file);

            QDateTime current_date_time = QDateTime::currentDateTime();
            QString current_date = current_date_time.toString("MM月dd日 hh时mm分ss秒 \t");
            QString message = current_date \
                    + "电压:\t" \
                    + QString::number(voltage) + "\t" \
                    + "电流:\t" \
                    + QString::number(current) + "\t" \
                    + "百分比:\t" \
                    + QString::number(present) + "\t" \
                    + "余量:\t" \
                    + QString::number(leave_power) + "\t";
            text_stream << message << "\r\n";
            file.flush();
            file.close();
        }
    } else {
        count++;
    }
}

void Widget::slot_socket_read_data()
{
    QByteArray buffer;
    //读取缓冲区数据
    buffer = socket->readAll();
    if(!buffer.isEmpty())
    {
//        qDebug() << buffer.size();
        protocol_value->unpack(buffer);
    }
    socket->flush();
}

void Widget::slot_connect()
{
    this->write_log(__FUNCTION__);
    if(btn_connect->text() == tr("连接"))
    {
        QString IP;
        qint16 port;
        //获取IP地址
        IP = line_ip->text();
        //获取端口号 调用QString类的成员函数
        port = line_port->text().toInt();
        //取消已有连接
        socket->abort();
        //连接服务器 读写模式
        socket->connectToHost(IP,port);
        //等待连接成功
        if(!socket->waitForConnected(3000))
        {
            write_log("连接失败");
            display_text(QString("----------连接失败----------"));
            return;
        }
        write_log("连接成功");
        display_text(QString("----------连接成功----------"));
        //使能按键
        set_btn_enable(true);
        //修改按键文字
        btn_connect->setText("断开连接");
        btn_connect->setStyleSheet("color:#ff0000");
    }
    else
    {
        //断开连接
        socket->disconnectFromHost();
        write_log("断开连接");
        display_text(QString("----------断开连接----------"));
        set_btn_enable(false);
        btn_connect->setText("连接");
        btn_connect->setStyleSheet("color:#0000ff");
        status_clear();
    }
}

void Widget::slot_disconnect()
{
    write_log(__FUNCTION__);
    //修改按键文字，加上去之后就重复了,但可保证意外断开
    write_log("断开连接");
    display_text(QString("----------断开连接----------"));
    set_btn_enable(false);
    btn_connect->setText("连接");
    btn_connect->setStyleSheet("color:#0000ff");
    status_clear();
}

void Widget::slot_send()
{
    this->write_log(__FUNCTION__);
    if(line_send->text().toLongLong() != NULL)
    {
        //获取文本框内容并以ASCII码形式发送
        QByteArray temp = line_send->text().toLatin1();
        QByteArray data;
        int flag = 1; //1表示高位 0表示低位
        int num = 0;
        for(auto i : temp)
        {
            if(i != ' ')
            {
                if(flag == 1) {
                    //高位
                    num += char2int(i)*16;
                    flag = 0;
                } else {
                    //flag == 0;
                    num += char2int(i);
                    flag = 1;
                    data.append((uint8_t)num);
                    num = 0;
                }
            }
        }
        send(data);
    }
    else
    {
        display_text(QString("-------空的你发个锤子-------"));
    }
}

void Widget::slot_timeout()
{
    this->update_ui();

    static int count_1s = 0;
    if (count_1s < 1000 / timer_frequence){
        count_1s++;
    } else {
        this->protocol_value->update_frequency();
        count_1s = 0;
    }
}

void Widget::slot_set_log_path()
{
    logpath = QFileDialog::getExistingDirectory(this, "选择日志路径", logpath) + "/battery.txt";
    line_log->setText(logpath);
    radio_log->setChecked(true);
}

void Widget::slot_comboBox(QString str)
{
    if(str == strlist_IP.at(0))
    {
        line_ip->setText("192.168.8.31");
        line_port->setText("9000");
    }
    else if(str == strlist_IP.at(1))
    {
        line_ip->setText("192.168.8.30");
        line_port->setText("9000");
    }
    else if(str == strlist_IP.at(2))
    {
        line_ip->setText("192.168.8.30");
        line_port->setText("9001");
    }
    else if(str == strlist_IP.at(3))
    {
        line_ip->setText("127.0.0.1");
        line_port->setText("9000");
    }
    else{}
}

void Widget::slot_send_date(QByteArray date, int percent)
{

}

void Widget::slot_erase_flash()
{

}

void Widget::slot_update_successful()
{

}

void Widget::slot_pushButton_send_ackerman()
{
    this->write_log(__FUNCTION__);
    uint8_t mode = 0;

    if (ack_mode->currentText() == strlist_ack_mode.at(0))
        mode = 1;
    else if (ack_mode->currentText() == strlist_ack_mode.at(1))
        mode = 2;
    else if (ack_mode->currentText() == strlist_ack_mode.at(2))
        mode = 3;
    else if (ack_mode->currentText() == strlist_ack_mode.at(3))
        mode = 4;
    else if (ack_mode->currentText() == strlist_ack_mode.at(4))
        mode = 5;
    else if (ack_mode->currentText() == strlist_ack_mode.at(5))
        mode = 2;
    else if (ack_mode->currentText() == strlist_ack_mode.at(6))
        mode = 3;
    else if (ack_mode->currentText() == strlist_ack_mode.at(7))
        mode = 0;

    QByteArray data = protocol_value->protocol_ackerman_pack(mode,
                                                             ack_v->value(),
                                                             ack_w->value(),
                                                             ack_r->value(),
                                                             ack_deg->value());
    send(data);
}

void Widget::slot_pushButton_send_ackerman_stop()
{
    this->write_log(__FUNCTION__);
    QByteArray data = protocol_value->protocol_ackerman_pack(1,
                                                             0,
                                                             0,
                                                             0,
                                                             0);
    send(data);
}

void Widget::slot_pushButton_send_fine()
{
    this->write_log(__FUNCTION__);
    QByteArray data = protocol_value->protocol_fine_pack(fine_speed->value_lf(),
                                                         fine_speed->value_rf(),
                                                         fine_speed->value_lb(),
                                                         fine_speed->value_rb(),
                                                         fine_degree->value_lf(),
                                                         fine_degree->value_rf(),
                                                         fine_degree->value_lb(),
                                                         fine_degree->value_rb());
    send(data);
}

void Widget::slot_pushButton_send_fine_stop()
{
    this->write_log(__FUNCTION__);
    QByteArray data = protocol_value->protocol_fine_pack(0,
                                                         0,
                                                         0,
                                                         0,
                                                         0,
                                                         0,
                                                         0,
                                                         0);
    send(data);
}

void Widget::slot_pushButton_reset_odom()
{
    this->write_log(__FUNCTION__);
    QByteArray data = protocol_value->protocol_reset_odom_pack(odom_clear_x->value(),
                                                               odom_clear_y->value(),
                                                               odom_clear_yaw->value());
    send(data);
}

void Widget::slot_pushButton_send_mode()
{
    this->write_log(__FUNCTION__);
    uint8_t mode = 0;
    if (motion_mode->currentText() == strlist_motion_mode.at(0))
        mode = 1;
    else if (motion_mode->currentText() == strlist_motion_mode.at(1))
        mode = 2;
    else if (motion_mode->currentText() == strlist_motion_mode.at(2))
        mode = 3;
    QByteArray data = protocol_value->protocol_motion_mode_pack(mode);
    send(data);
}

void Widget::slot_pushButton_send_cmd()
{
    this->write_log(__FUNCTION__);
    uint8_t mode = 0;
    if (cmd->currentText() == strlist_cmd.at(0))
        mode = 1;
    else if (cmd->currentText() == strlist_cmd.at(1))
        mode = 2;
    else if (cmd->currentText() == strlist_cmd.at(2))
        mode = 3;
    QByteArray data = protocol_value->protocol_cmd_pack(mode);
    send(data);
}

void Widget::slot_set_light()
{
    this->write_log(__FUNCTION__);
    QByteArray data = protocol_value->protocol_state_pack(light_flag);
    lcd_light->display(light_flag);
    light_flag++;
    if(light_flag > 12) light_flag = 0;
    send(data);
}

void Widget::slot_version()
{
    this->write_log(__FUNCTION__);
}

void Widget::slot_update()
{
    emit this->signal_go_update(filepath);
}

void Widget::slot_getfile()
{
    write_log(__FUNCTION__);
    filepath = QFileDialog::getOpenFileName(this, "选择升级文件", filepath,"*.rbl");
    this->update_wig->line_path->setText(filepath);
}

void Widget::send(QByteArray data)
{
    if (socket->state() == QAbstractSocket::ConnectedState) {
        socket->write(data);
        display_text(data, 0);
        socket->flush();
    } else {
        display_text(QString("----------未连接，无法发送----------"));
    }
}

void Widget::display_text(QString text)
{
    QString current_date = QDateTime::currentDateTime().toString("[hh:mm:ss:zzz] ");
    edit->appendPlainText(current_date + text);
}

void Widget::display_text(QByteArray SendData, int direction)
{
    QString current_date = QDateTime::currentDateTime().toString("[hh:mm:ss:zzz] ");
    if(direction == 0) {
        edit->appendPlainText(current_date + "send:");
    } else {
        edit->appendPlainText(current_date + "recv:");
    }

    QTextCursor cursor = edit->textCursor();
    cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);   //移动到文档的结尾。
    edit->setTextCursor(cursor);

    for (int i=0; i<SendData.length(); i++)
    {
        edit->insertPlainText(SendData.mid(i,1).toHex());
        edit->insertPlainText(" ");
    }
}

int Widget::char2int(char ch)
{
    if ('0' <= ch && ch <= '9')
        return ch - '0';
    else if ('A' <= ch && ch <= 'F')
        return ch - 'A' + 10;
    else if ('a' <= ch && ch <= 'f')
        return ch - 'a' + 10;
    else
        return -1;
}

void Widget::update_ui()
{
    if (socket->state() == QAbstractSocket::ConnectedState)
    {
        // 刷新率
        lcd_dipan_heart->display(protocol_value->frequency[type_dipan_pc_heart]);
        lcd_ult->display(protocol_value->frequency[type_dipan_pc_ult]);
        lcd_other->display(protocol_value->frequency[type_dipan_pc_other]);
        lcd_encoder->display(protocol_value->frequency[type_dipan_pc_encoder]);
        lcd_speed->display(protocol_value->frequency[type_dipan_pc_speed]);
        lcd_dipan_reply->display(protocol_value->frequency[type_dipan_pc_reply]);
        lcd_imu->display(protocol_value->frequency[type_dipan_pc_imu]);
        lcd_dipan_status->display(protocol_value->frequency[type_dipan_pc_status]);

        lcd_dianyuan_heart->display(protocol_value->frequency[type_dianyuan_pc_heart]);
        lcd_battery->display(protocol_value->frequency[type_dianyuan_pc_battery]);
        lcd_dianyuan_status->display(protocol_value->frequency[type_dianyuan_pc_status]);
        lcd_dianyuan_reply->display(protocol_value->frequency[type_dianyuan_pc_reply]);
        lcd_radar->display(protocol_value->frequency[type_dianyuan_pc_radar]);

        //超声数据更新
        ult->setNum(protocol_value->data_dipan_pc_ult.lf,
                    protocol_value->data_dipan_pc_ult.rf,
                    protocol_value->data_dipan_pc_ult.lb,
                    protocol_value->data_dipan_pc_ult.rb);

        //安全触边
        edge->setNum(protocol_value->data_dipan_pc_other.safe_edge_front,
                     protocol_value->data_dipan_pc_other.safe_edge_back);

        //轮子状态 0 正常 1 失联 2 错误
        line_state->setText(get_wheel_state(protocol_value->data_dipan_pc_other.state_line_lf),
                            get_wheel_state(protocol_value->data_dipan_pc_other.state_line_rf),
                            get_wheel_state(protocol_value->data_dipan_pc_other.state_line_lb),
                            get_wheel_state(protocol_value->data_dipan_pc_other.state_line_rb));
        turn_state->setText(get_wheel_state(protocol_value->data_dipan_pc_other.state_roll_lf),
                            get_wheel_state(protocol_value->data_dipan_pc_other.state_roll_rf),
                            get_wheel_state(protocol_value->data_dipan_pc_other.state_roll_lb),
                            get_wheel_state(protocol_value->data_dipan_pc_other.state_roll_rb));

        //遥控器状态
        QString t1, t2;
        if((protocol_value->data_dipan_pc_other.vel_controller_state >> 7) == 1)
            t1 = "在线"; // 1 在线 0 离线
        else
            t1 = "离线"; // 1 在线 0 离线

        if((protocol_value->data_dipan_pc_other.vel_controller_state & 0xF) == 0)
            t2 = "自动"; // 0 自动 1 手动 2 停止
        else if((protocol_value->data_dipan_pc_other.vel_controller_state & 0xF) == 1)
            t2 = "手动"; // 0 自动 1 手动 2 停止
        else
            t2 = "停止"; // 0 自动 1 手动 2 停止
        mc6c->setText(t1, t2);

        // 舵机
        steer->set_status(protocol_value->data_dipan_pc_other.steer_engine_state);

        //编码器
        line_v->setNum(protocol_value->data_dipan_pc_encoder.speed_lf,
                       protocol_value->data_dipan_pc_encoder.speed_rf,
                       protocol_value->data_dipan_pc_encoder.speed_lb,
                       protocol_value->data_dipan_pc_encoder.speed_rb);

        turn_deg->setNum(protocol_value->data_dipan_pc_encoder.degrees_lf,
                         protocol_value->data_dipan_pc_encoder.degrees_rf,
                         protocol_value->data_dipan_pc_encoder.degrees_lb,
                         protocol_value->data_dipan_pc_encoder.degrees_rb);

        encoder->setNum(protocol_value->data_dipan_pc_encoder.line_lf,
                        protocol_value->data_dipan_pc_encoder.line_rf,
                        protocol_value->data_dipan_pc_encoder.line_lb,
                        protocol_value->data_dipan_pc_encoder.line_rb);

        //里程计
        distance->setNum(protocol_value->data_dipan_pc_speed.p_x,
                         protocol_value->data_dipan_pc_speed.p_y);

        speed->setNum(protocol_value->data_dipan_pc_speed.v_x,
                      protocol_value->data_dipan_pc_speed.v_y);

        degree->setNum(protocol_value->data_dipan_pc_speed.w_z,
                       protocol_value->data_dipan_pc_speed.deg_z);

        QString t3, t4;
        if(protocol_value->data_dipan_pc_speed.mode == 1)
            t3 = "四轮ackman"; // 1: 四轮ackman 2: 两轮ackman 3: 差速
        else if(protocol_value->data_dipan_pc_speed.mode == 2)
            t3 = "两轮ackman"; // 1: 四轮ackman 2: 两轮ackman 3: 差速
        else
            t3 = "差速"; // 1: 四轮ackman 2: 两轮ackman 3: 差速
        if(protocol_value->data_dipan_pc_speed.stop_flag == 0)
            t4 = "正常"; // 0: 正常 1：停车
        else
            t4 = "停车"; // 0: 正常 1：停车

        flag->setText(t3, t4);


        //IMU
        imu_degree->setNum(protocol_value->data_dipan_pc_imu.roll,
                           protocol_value->data_dipan_pc_imu.pitch,
                           protocol_value->data_dipan_pc_imu.yaw);

        imu_acc->setNum(protocol_value->data_dipan_pc_imu.acc_x,
                        protocol_value->data_dipan_pc_imu.acc_y,
                        protocol_value->data_dipan_pc_imu.acc_z);

        imu_gyro->setNum(protocol_value->data_dipan_pc_imu.gyro_x,
                         protocol_value->data_dipan_pc_imu.gyro_y,
                         protocol_value->data_dipan_pc_imu.gyro_z);

        imu_magic->setNum(protocol_value->data_dipan_pc_imu.mag_x,
                          protocol_value->data_dipan_pc_imu.mag_y,
                          protocol_value->data_dipan_pc_imu.mag_z);

        //电池
        charge_flag->set_num(protocol_value->data_dianyuan_pc_battery.charge_flag);
        current->set_num(protocol_value->data_dianyuan_pc_battery.current);
        voltage->set_num(protocol_value->data_dianyuan_pc_battery.voltage);
        remain_power->set_num(protocol_value->data_dianyuan_pc_battery.leave_power);
        sum_power->set_num(protocol_value->data_dianyuan_pc_battery.sum_power);
        percent->set_num(protocol_value->data_dianyuan_pc_battery.percent);


        //开关
        int i = 0;
        for(auto it : list_sw)
        {
            it->set_status((int)((protocol_value->data_dianyuan_pc_status.switch_flag >> i) & 0x01));
            i++;
        }

        //雷达
        for(int i = 0; i < protocol_value->data_dianyuan_pc_radar_f.point_number; i++)
        {
            table_mode_f->setItem(i, 0, new QStandardItem(QString::number(protocol_value->data_dianyuan_pc_radar_f.point[i].Objects_ID)));
            table_mode_f->setItem(i, 1, new QStandardItem(QString::number(protocol_value->data_dianyuan_pc_radar_f.point[i].Objects_DistLong)));
            table_mode_f->setItem(i, 2, new QStandardItem(QString::number(protocol_value->data_dianyuan_pc_radar_f.point[i].Objects_DistLat)));
            table_mode_f->setItem(i, 3, new QStandardItem(QString::number(protocol_value->data_dianyuan_pc_radar_f.point[i].Objects_VrelLong)));
            table_mode_f->setItem(i, 4, new QStandardItem(QString::number(protocol_value->data_dianyuan_pc_radar_f.point[i].Objects_VrelLat)));
        }

        for(int i = 0; i < protocol_value->data_dianyuan_pc_radar_b.point_number; i++)
        {
            table_mode_b->setItem(i, 0, new QStandardItem(QString::number(protocol_value->data_dianyuan_pc_radar_b.point[i].Objects_ID)));
            table_mode_b->setItem(i, 1, new QStandardItem(QString::number(protocol_value->data_dianyuan_pc_radar_b.point[i].Objects_DistLong)));
            table_mode_b->setItem(i, 2, new QStandardItem(QString::number(protocol_value->data_dianyuan_pc_radar_b.point[i].Objects_DistLat)));
            table_mode_b->setItem(i, 3, new QStandardItem(QString::number(protocol_value->data_dianyuan_pc_radar_b.point[i].Objects_VrelLong)));
            table_mode_b->setItem(i, 4, new QStandardItem(QString::number(protocol_value->data_dianyuan_pc_radar_b.point[i].Objects_VrelLat)));
        }

        radar_front->setModel(table_mode_f);
        radar_back->setModel(table_mode_b);

        //日志记录
        note_battery(protocol_value->data_dianyuan_pc_battery.voltage, \
                     protocol_value->data_dianyuan_pc_battery.current, \
                     protocol_value->data_dianyuan_pc_battery.percent, \
                     protocol_value->data_dianyuan_pc_battery.leave_power);

    }
    this->update();
}

QString Widget::get_wheel_state(int num)
{
    if(num == 0)
        return "正常";
    else if(num == 1)
        return "失联";
    else
        return "错误";
}

